package com.practice.java8.methodreference;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class FunctionMain {

	public static void main(String[] args) {
		Entity entity = new Entity(8);
		Factor factor = new Factor(2);
		Result result;
		
		Action executeByInstance = factor::clear;
		Action executeByStatic = Util::idle;
		Action executeByConstructor = null; // Need output parameter.
		Action executeByClass = null; // Need input parameter.
		Consumer<Factor> acceptFactorByInstance = entity::merge;
		Consumer<Factor> acceptFactorByStatic = Util::touch;
		Consumer<Factor> acceptFactorByConstructor = null; // Need output parameter.
		Consumer<Factor> acceptFactorByClass = Factor::clear;
		Supplier<Result> getResultByInstance = entity::conclude;
		Supplier<Result> getResultByStatic = Util::fancy;
		Supplier<Result> getResultByConstructor = Result::new;
		Supplier<Result> getResultByClass = null; // Need input parameter.
		Function<Factor, Result> convertFactorToResultByInstance = entity::join;
		Function<Factor, Result> convertFactorToResultByStatic = Util::extract;
		Function<Entity, Result> convertEntityToResultByConstructor = Result::new;
		Function<Entity, Result> convertEntityToResultByClass = Entity::conclude;
		BiFunction<Entity, Factor, Result> combineEntityFactorToResultByClass = Entity::join;
		BiFunction<Entity, Factor, Result> combineEntityFactorToResultByStatic = Util::recover;
		BiFunction<Entity, Factor, Result> combineEntityFactorToResultByConstructor = Result::new;
		
		executeByInstance.execute();
		executeByStatic.execute();
		
		acceptFactorByInstance.accept(factor);
		acceptFactorByStatic.accept(factor);
		acceptFactorByClass.accept(factor);
		
		result = getResultByInstance.get();
		result = getResultByStatic.get();
		result = getResultByConstructor.get();
		
		result = convertFactorToResultByInstance.apply(factor);
		result = convertFactorToResultByStatic.apply(factor);
		result = convertEntityToResultByConstructor.apply(entity);
		result = convertEntityToResultByClass.apply(entity);
		
		result = combineEntityFactorToResultByClass.apply(entity, factor);
		result = combineEntityFactorToResultByStatic.apply(entity, factor);
		result = combineEntityFactorToResultByConstructor.apply(entity, factor);
	}

	static class Entity {

		private int number;
		
		public Entity() {
			this.number = 1;
		}

		public Entity(int number) {
			this.number = number;
		}

		public Result conclude() {
			return new Result(this.number);
		}
		
		public Result join(Factor factor) {
			return new Result(this.number * factor.number);
		}
		
		public void merge(Factor factor) {
			this.number *= factor.number;
		}

	}

	static class Factor {
		
		private int number;
		
		public Factor() {
			this.number = 1;
		}

		public Factor(int number) {
			this.number = number;
		}
		
		public Factor(Entity entity) {
			this.number = entity.number;
		}
		
		public void clear() {
			this.number = 1;
		}

	}

	static class Result {
		
		private int number;
		
		public Result() {
			this.number = 1;
		}

		public Result(int number) {
			this.number = number;
		}
		
		public Result(Entity entity) {
			this.number = entity.number;
		}
		
		public Result(Entity entity, Factor factor) {
			this.number = entity.number * factor.number;
		}
		
		public void print() {
			System.out.println(this.number);
		}

	}
	
	static class Util {
		
		public static void idle() {
			
		}
		
		public static Result fancy() {
			return new Result(0);
		}
		
		public static void touch(Factor factor) {
			
		}
		
		public static Result extract(Factor factor) {
			return new Result(factor.number);
		}
		
		public static Result recover(Entity entity, Factor factor) {
			return new Result(entity.number / factor.number);
		}
		
	}
	
	@FunctionalInterface
	static interface Action {
		void execute();
	}
	
}
